package com.demo.springmvc底层演示.demo2;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.MethodParameter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockPart;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.bind.support.DefaultDataBinderFactory;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver;
import org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver;
import org.springframework.web.method.annotation.RequestParamMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
import org.springframework.web.servlet.mvc.method.annotation.ServletCookieValueMethodArgumentResolver;
import org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor;
import org.springframework.web.servlet.mvc.method.annotation.ServletRequestDataBinderFactory;
import org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolver;

import com.demo.springmvc底层演示.demo1.User;

// request参数处理
public class Test {
	
	public static void main(String[] args) throws Exception {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(WebConfig.class);
		// 准备测试request
		HttpServletRequest request = mockRequest();
		// 控制器方法被封装为HandlerMethod
		HandlerMethod handlerMethod = new HandlerMethod(new Controller(), Controller.class.getMethod("test", String.class, String.class, int.class, String.class, MultipartFile.class, int.class, String.class, String.class, String.class, HttpServletRequest.class, User.class, User.class, User.class));
		// 准备对象绑定与类型转换：把请求参数和方法参数绑定，必要时进行类型转换
		DefaultDataBinderFactory factory = new ServletRequestDataBinderFactory(null, null);
		// 准备ModelAndViewContainer用于存储中间Model结果
		ModelAndViewContainer container = new ModelAndViewContainer();
		// 解析每个参数值
		for(MethodParameter parameter : handlerMethod.getMethodParameters()) {
			// 多个解析器组合
			HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite();
			List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>() {{
				add(new MappingJackson2HttpMessageConverter());
			}};
			composite.addResolvers(
				// false: 表示必须有@RequestParam注解的参数才会被解析；true: 不写也会尝试解析
				new RequestParamMethodArgumentResolver(context.getBeanFactory(), false),
				new PathVariableMethodArgumentResolver(),
				new RequestHeaderMethodArgumentResolver(context.getBeanFactory()),
				new ServletCookieValueMethodArgumentResolver(context.getBeanFactory()),
				new ExpressionValueMethodArgumentResolver(context.getBeanFactory()),
				new ServletRequestMethodArgumentResolver(),
				new ServletModelAttributeMethodProcessor(false),
				new RequestResponseBodyMethodProcessor(converters),
				new ServletModelAttributeMethodProcessor(true),
				new RequestParamMethodArgumentResolver(context.getBeanFactory(), true)
			);
			
			String annotations = Arrays.stream(parameter.getParameterAnnotations()).map(a->a.annotationType().getSimpleName()).collect(Collectors.joining());
			String str = annotations.length()>0 ? " @" + annotations + " ": " ";
			// 参数名解析器
			parameter.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
			
			if(composite.supportsParameter(parameter)) {
				// 支持此参数
				Object v = composite.resolveArgument(parameter, container, new ServletWebRequest(request), factory);
				System.out.println("[" + parameter.getParameterIndex() + "]" + str + parameter.getParameterType().getSimpleName() + " " + parameter.getParameterName() + "->" + v.getClass().getSimpleName() + " " + v);
			} else {
				System.out.println("[" + parameter.getParameterIndex() + "]" + str + parameter.getParameterType().getSimpleName() + " " + parameter.getParameterName());
			}
		}
	}
	
	private static HttpServletRequest mockRequest() throws Exception {
		MockHttpServletRequest request = new MockHttpServletRequest();
		request.setParameter("name1", "zhangsan");
		request.setParameter("name2", "lisi");
		request.addPart(new MockPart("file", "abc", "hello".getBytes("utf-8")));
		Map<String, String> uriTemplateVariables = new AntPathMatcher().extractUriTemplateVariables("/test/{id}", "/test/123");
		request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables);
		request.setContentType("application/json");
		request.setCookies(new Cookie("token", "123456"));
		request.setParameter("name", "张三");
		request.setParameter("age", "18");
		request.setContent("{\"name\":\"李四\",\"age\":20}".getBytes("utf-8"));
		return new StandardServletMultipartResolver().resolveMultipart(request);
	}

}
